package ru.ipo.dces.problemsviewer;

import com.jgoodies.looks.plastic.Plastic3DLookAndFeel;
import ru.ipo.dces.PluginsRegistry;
import ru.ipo.dces.debug.PersistentStorage;
import ru.ipo.dces.debug.ServerEmulator;
import ru.ipo.dces.pluginapi.Plugin;
import ru.ipo.dces.pluginapi.PluginEnvironment;
import ru.ipo.problemsapi.ExtendedProblem;
import ru.ipo.problemsapi.ZipProblem;

import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;

/**
 * Created by IntelliJ IDEA.
 * User: ilya
 * Date: 29.05.11
 * Time: 1:46
 */
public class ProblemsViewer extends JFrame implements ActionListener {

    private PersistentStorage storage = new PersistentStorage(".problems.viewer.storage");
    private JFileChooser problemChooser;
    private JTabbedPane tabbedPane = new JTabbedPane();
    private int problemIndex = 1;

    private static PluginsRegistry pluginsRegistry = new PluginsRegistry(
            Settings.getInstance().getPluginsFolder(),
            null,
            "ru.ipo.dces.debug.serverplugins"
    );
    private final String PROBLEMS_DIRECTORY_STORAGE = "problems directory";

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ProblemsViewer();
            }
        });
    }

    public ProblemsViewer() {
        try {
            UIManager.setLookAndFeel(new Plastic3DLookAndFeel());
        } catch (Exception e) {
            System.err.println("Не удалось загрузить L&F");
            System.exit(1);
        }

        setTitle("Просмотр задач");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(800, 600);
        setLocationRelativeTo(null);
        setFocusable(true);

        createProblemChooser();

        initInterface();

        setVisible(true);
    }

    private void createProblemChooser() {
        String directory = storage.getData(PROBLEMS_DIRECTORY_STORAGE);
        problemChooser = new JFileChooser(directory);

        FileFilter problemFilesFilter = new FileFilter() {
            @Override
            public boolean accept(File f) {
                return f.isDirectory() || f.getName().toLowerCase().endsWith(".problem");
            }

            @Override
            public String getDescription() {
                return "Файлы задач (*.problem)";
            }
        };

        problemChooser.addChoosableFileFilter(problemFilesFilter);

        problemChooser.setFileFilter(problemFilesFilter);
    }

    private void initInterface() {
        setLayout(new BorderLayout());
        add(tabbedPane);

        OpenButton openButton = new OpenButton();

        tabbedPane.addTab(null, new BigBorderPanel(openButton));
        ProblemHeaderAddTabComponent addTabComponent = new ProblemHeaderAddTabComponent(tabbedPane);
        tabbedPane.setTabComponentAt(0, addTabComponent);

        openButton.addActionListener(this);
        addTabComponent.addActionListener(this);
    }

    public void paintComponents(Graphics g) {
        super.paintComponents(g);
    }

    //this action means clicking open problem button
    public void actionPerformed(ActionEvent e) {
        if (problemChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            storage.setData(PROBLEMS_DIRECTORY_STORAGE, problemChooser.getSelectedFile().getParent());
            storage.flush();
            //load problem
            try {
                ZipProblem problem = new ZipProblem(problemChooser.getSelectedFile());
                addTabWithProblem(problem);
            } catch (IOException e1) {
                JOptionPane.showMessageDialog(
                        this,
                        "Не удалось загрузить задачу из файла" + problemChooser.getSelectedFile(),
                        "Ошибка загрузки файла",
                        JOptionPane.ERROR_MESSAGE
                );
            }
        }
    }

    private void addTabWithProblem(ZipProblem problem) {
        ExtendedProblem p = new ExtendedProblem(problem);

        int newTabIndex;

        try {
            //get two plugins
            Class<? extends Plugin> clientPluginClass = pluginsRegistry.getPlugin(
                    Plugin.class,
                    p.getClientPlugin()
            );

            Class<? extends ServerEmulator> serverPluginClass = pluginsRegistry.getPlugin(
                    ServerEmulator.class,
                    p.getServerPlugin()
            );

            //create serverPlugin
            ServerEmulator serverPlugin = serverPluginClass.newInstance();

            //create environment
            ProblemSubmissionHistory history = new ProblemSubmissionHistory();
            PluginEnvironmentImpl pe = new PluginEnvironmentImpl(
                    this,
                    serverPlugin,
                    new ExtendedProblem(problem),
                    history
            );

            //create clientPlugin
            Constructor<? extends Plugin> constructor = clientPluginClass.getConstructor(PluginEnvironment.class);
            Plugin clientPlugin = constructor.newInstance(pe);

            JPanel pluginPanel = clientPlugin.getPanel();

            //create tab component
            TabComponent tabComponent = new TabComponent(pluginPanel, history, pe);

            newTabIndex = addTab(tabComponent);
        } catch (Exception e) {
            //TODO make meaningful error descriptions
            newTabIndex = addTab(new BigBorderPanel(new ErrorComponent(e)));
        }

        tabbedPane.setSelectedIndex(newTabIndex);
    }

    private int addTab(JComponent component) {
        int newIndex = tabbedPane.getTabCount() - 1;
        tabbedPane.insertTab("Задача " + problemIndex, null, component, null, newIndex);
        problemIndex++;

        ProblemHeaderComponent header = new ProblemHeaderComponent(tabbedPane);
        tabbedPane.setTabComponentAt(newIndex, header);

        header.getCloseButton().addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ProblemHeaderComponent h = ((ProblemHeaderComponent.CloseButton) e.getSource()).getHeader();
                int ind = tabbedPane.indexOfTabComponent(h);
                tabbedPane.removeTabAt(ind);
            }
        });

        header.getHistoryButton().addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                ProblemHeaderComponent.HistoryButton source = (ProblemHeaderComponent.HistoryButton) e.getSource();
                ProblemHeaderComponent h = source.getHeader();
                int ind = tabbedPane.indexOfTabComponent(h);
                Component component = tabbedPane.getComponentAt(ind);
                if (component instanceof TabComponent) {
                    TabComponent tabComponent = (TabComponent) component;
                    tabComponent.setHistoryVisible(source.isSelected());
                }
            }
        });

        return newIndex;
    }

    public JTabbedPane getTabbedPane() {
        return tabbedPane;
    }

    public int indexOfTabByPluginEnvironment(PluginEnvironment environment) {
        int tabs = tabbedPane.getTabCount();
        for (int tab = 0; tab < tabs; tab++) {
            Component component = tabbedPane.getTabComponentAt(tab);
            if (component instanceof TabComponent
                    &&
                    ((TabComponent) component).getEnvironment() == environment
                    )
                return tab;
        }
        return -1;
    }
}